<template>
  <div>
    <el-upload
      ref="upload"
      multiple="multiple"
      :show-file-list="false"
      :on-change="handleChange"
      :on-exceed="handleExceed"
      :auto-upload="false"
      action=""
      :disabled="beginUpload">
      <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
      <el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button>
      <span slot="tip" class="el-upload__tip" style="padding-left: 25px; color: #F56C6C;">只能上传mp4/avi文件，且不超过10G，文件数量最多10个</span>
    </el-upload>

    <ul class="file-list" v-loading="loading" element-loading-target="loadingEl">
      <li class="file-item" v-for="(file, index) in fileList" :key="index">
        <div class="file-name"><i class="el-icon-paperclip"></i> {{ file.name }}</div>
        <div class="upload-progress"><el-progress :percentage="file.percentage || 0"></el-progress></div>
        <div class="file-status">{{statusInfoMap[file.uploadStatus+'']}}</div>
        <div class="tips">{{file.tips}}</div>
        <div class="remove-item" @click="handleRemove(index,fileList)">×</div>
      </li>
    </ul>
  </div>
</template>

<script>
    import axios from 'axios'
    import apis from '../apis'
    import SparkMD5 from 'spark-md5'

    // 接口信息
    let Interfaces = {
        fileMetaData(miniPartFile,filename,fileSize,md5){
            let formData = new FormData();
            formData.append('partFile',miniPartFile,filename);
            formData.append('fileSize',fileSize);
            formData.append('md5',md5);
            return axios.post('/company/media/upload/fileMetaData',formData,apis.headers.formData);
        },
        upload2Sftp(partFile,relativePath,uuid){
            let formData = new FormData();
            formData.append('file',partFile);
            formData.append('relativePath',relativePath);
            formData.append('uuid',uuid);
            return axios.post('/company/media/upload/upload2Sftp',formData,apis.headers.formData);
        },
        checkIntegrity(relativePath,uuid){
            return axios.get('/company/media/upload/checkIntegrity', Object.assign({params:{uuid,relativePath}},apis.headers.urlencoded));
        },
        breakPointFiles(){
            return axios.get('/company/media/upload/breakPointFiles',apis.headers.urlencoded);
        },
        dropBreakPointFile(relativePath,uuid){
            return axios.post('/company/media/upload/breakPointFiles',{uuid,relativePath},apis.headers.urlencoded);
        }
    };

    const shardSize = 1024 * 1024 * 2;                      // 分片上传，每片大小 2M
    const chunkSize = 1024 * 1024 * 4;                      // md5 计算每段大小 4M
    const statusInfoMap = {'0':'待上传','1':'正在计算','2':'正在上传','3':'上传成功','4':'上传失败','5':'暂停上传','6':'文件检查'};

    export default {
        name: "BigFileUpload",
        data(){
            return {
                fileList:[],
                loading:false,
                statusInfoMap,
                beginUpload:false
            }
        },
        mounted(){
            console.log('加载断点文件')
            // 加载断点文件
            Interfaces.breakPointFiles().then(res => {
                if(res.data ){
                    let map = res.data.data.map(fileItem => {return {name:fileItem.originFileName,uploadStatus:5,raw:undefined,relativePath:fileItem.relativePath,uuid:fileItem.uuid}});
                    this.fileList = [].concat.apply([], map);
                }
            });
        },
        methods:{

            handleChange(file, fileList){
                fileList.forEach(file => {this.$set(file, 'uploadStatus', 0)});
                this.fileList = fileList;
            },
            handleRemove(index, fileList) {
                fileList.splice(index,1);
            },
            handleExceed(files, fileList) {
                //超出文件个数钩子
            },
            // 单个文件上传
            async uploadFileItem(uploadFile){
                let file = uploadFile.raw;
                let offset = uploadFile.uploaded || 0;
                let shardCount =Math.ceil((file.size - offset )/shardSize);
                for(let i=0;i<shardCount;i++){
                    let start = i * shardSize + offset;
                    let end = Math.min(file.size,start + shardSize );//在file.size和start+shardSize中取最小值，避免切片越界
                    let filePart = file.slice(start,end);
                    await Interfaces.upload2Sftp(filePart,uploadFile.relativePath,uploadFile.uuid).then(res => {
                        if(res.data.code != '00000'){
                            uploadFile.uploadStatus = 4;
                            uploadFile.tips = res.data.message;
                            return false;
                        }
                        let uploaded = res.data.data;
                        //进度计算
                        let process = parseFloat((uploaded / file.size) * 100) | 0
                        uploadFile.percentage = process;

                        console.log(file.name+'|'+process);

                        if(uploaded === file.size){
                            // 上传完成后，检查文件完整性
                            uploadFile.uploadStatus = 6;
                            Interfaces.checkIntegrity(uploadFile.relativePath,uploadFile.uuid).then(res => {
                                if(res.data.code == '00000'){
                                    uploadFile.uploadStatus = 3;
                                }else{
                                    uploadFile.uploadStatus = 4;
                                    uploadFile.tips = res.data.message;
                                }
                            });
                        }
                    })
                }
            },
            // 开始上传事件
            submitUpload(){
                // 禁用文件 change
                this.beginUpload = true;

                // 开始上传
                if(this.fileList && this.fileList.length > 0){
                    for (let i=0;i<this.fileList.length;i++){
                        let fileInfo = this.fileList[i];
                        if(fileInfo.uploadStatus === 5 && !fileInfo.raw){
                            fileInfo.uploadStatus = 4;
                            fileInfo.tips = '上传失败，无文件信息';
                            continue;
                        }
                        if(fileInfo.uploadStatus === 5){
                            fileInfo.uploadStatus = 2;
                            this.uploadFileItem(fileInfo);
                            continue;
                        }
                        if(fileInfo.uploadStatus === 0){
                            // 从 0 开始上传的文件
                            checkFileItem(fileInfo.raw,res => {
                                console.log(res.data)
                                if(res.data.code !== '00000'){
                                    fileInfo.uploadStatus = 4;
                                    fileInfo.tips = res.data.message;
                                }else{
                                    if(res.data.data.secUpload){
                                        //如果是文件秒传
                                        fileInfo.uploadStatus = 3;
                                        fileInfo.tips = '文件秒传';
                                    }else{
                                        fileInfo.relativePath = res.data.data.relativePath;
                                        fileInfo.uuid = res.data.data.uuid;
                                        this.uploadFileItem(fileInfo);
                                    }
                                }

                            });

                            /**
                             * 计算 md5 值，请求后台查看是否可秒传
                             */
                            function checkFileItem(file,callback) {
                                md5Hex(file,function (md5) {
                                    // 读取文件前 20 字节
                                    Interfaces.fileMetaData(File.prototype.slice.call(file,0,20),file.name,file.size,md5).then(callback);
                                });
                            }
                        }
                    }
                }

                /**
                 * 计算 md5 值(同步计算)
                 * @param file
                 */
                function md5Hex(file,callback) {
                    let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
                        chunks = Math.ceil(file.size / chunkSize),
                        currentChunk = 0,

                        spark = new SparkMD5.ArrayBuffer(),
                        fileReader = new FileReader();

                    fileReader.onload = function (e) {
                        spark.append(e.target.result);                   // Append array buffer
                        currentChunk++;
                        if (currentChunk < chunks) {
                            loadNext();
                        } else {
                            let hash = spark.end();
                            callback(hash);
                        }
                    };

                    fileReader.onerror = function () {
                        console.warn('md5 计算时出错');
                    };

                    function loadNext(){
                        let start = currentChunk * chunkSize,
                            end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

                        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
                    }

                    loadNext();
                }
            }
        }
    }
</script>

<style lang="less" scoped>
  .file-list {
    min-height: 100px;
    padding: 0;
    font-size: 14px;
    list-style: none;
    .file-item {
      display: flex;
      padding: 5px;
      &:hover {
        background: #F5F7FA;
        color: #3a8ee6;
        .remove-item {
          opacity: 1;
        }
      }
      .file-name, .upload-progress, .file-status {
        width: 32%;
        padding-right: 10px;
        overflow: hidden;
        text-overflow:ellipsis;
        white-space: nowrap;
      }
      .remove-item {
        font-size: 20px;
        cursor: pointer;
        opacity: 0;
      }
    }
  }
</style>
